#ifndef GAME_CPP
#define GAME_CPP

#include "game.h"

bool Game::checkOver(Point p) {
    if (p.x < 1 || p.x > height || p.y < 1 || p.y > width)
        return true;
    if (map[p.x][p.y] && p != snake.points.back())
        return true;
    return false;
}

void Game::display() {
    system("cls");
    Point head = snake.points.front();
    for (int i = 0; i <= width+1; i++)
        printf("*");
    printf("\n");
    for (int i = 1; i <= height; i++) {
        printf("*");
        for (int j = 1; j <= width; j++) {
            if (Point(i, j) == food) {
                printf("$");
                continue;
            }
            if (Point(i, j) == head) {
                printf("@");
                continue;
            }
            printf("%c", map[i][j] ? '#' : ' ');
        }
        printf("*\n");
    }
    for (int i = 0; i <= width+1; i++)
        printf("*");
    printf("\nScores: %d\n", score);
    switch(status) {
        case 0:
            printf("Status: OK.\n");
            break;
        case 1:
            printf("Status: This step is useless.\n");
            break;
        case -1:
            printf("Status: The snake is dead.\n");
            break;
        case -2:
            printf("Status: The ANN is timeout. / The game is stopped manual.\n");
            break;
    }
}

Game::Game(const char *filename) : score(0), time(0), status(0) {
    FILE *fp = fopen(filename, "r");
    fscanf(fp, "%d%d%d", &height, &width, &initlen);
    fscanf(fp, "%d%d%d%d", &record.init_tail.x, &record.init_tail.y, &record.init_direction.x, &record.init_direction.y);
    int len;
    fscanf(fp, "%d", &len);
    for (int i = 0; i < len; i++) {
        int tmp;
        fscanf(fp, "%d", &tmp);
        record.operation.push_back(tmp);
    }
    fscanf(fp, "%d", &len);
    for (int i = 0; i < len; i++) {
        Point tmp;
        fscanf(fp, "%d%d", &tmp.x, &tmp.y);
        record.food.push_back(tmp);
    }
    fclose(fp);
    initReplay();
}

Game::Game(int height, int width, int initlen) : 
    score(0), time(0), status(0), 
    height(height), width(width), initlen(initlen) {
    for (int i = 0; i <= height; i++) {
        vector<bool> tmp;
        for (int j = 0; j <= width; j++)
            tmp.push_back(false);
        map.push_back(tmp);
    }

    Point tail = Point(random(initlen, height-initlen), random(initlen, width-initlen));
    snake.direction = numToDirection(random(1, 4));
    Point tmp = tail;
    for (int i = 1; i <= initlen; i++, tmp += snake.direction) {
        snake.points.push_front(tmp);
        map[tmp.x][tmp.y] = true;
    }
    
    record.init_tail = tail;
    record.init_direction = snake.direction;
    
    newFood();
}

int Game::go(int direction_num) {
    time++;
    record.operation.push_back(direction_num);
	
	Point new_direction = numToDirection(direction_num);
    if (new_direction == -snake.direction) {
        status = 1;
        return 1;
    }

    snake.direction = new_direction;
    Point head = snake.points.front();
    Point tail = snake.points.back();
    Point new_head = head + snake.direction;
    if (checkOver(new_head)) {
        status = -1;
        return -1;
    }
    // 先把尾巴删掉，防止将环形蛇误判为死亡 
    snake.points.pop_back();
    map[tail.x][tail.y] = false;
    snake.points.push_front(new_head);
    map[new_head.x][new_head.y] = true;

    if (new_head == food) {
        score++;
        // 吃食物后蛇的长度要增加，所以把刚才删掉的尾巴再加回来 
        snake.points.push_back(tail);
    	map[tail.x][tail.y] = true;
        newFood();
    }
    status = 0;
    return 0;
}

void Game::initReplay() {
	record.food_i = record.operation_i = 0;
	map.clear();
    for (int i = 0; i <= height; i++) {
        vector<bool> tmp;
        for (int j = 0; j <= width; j++)
            tmp.push_back(false);
        map.push_back(tmp);
    }

	Point tail = record.init_tail;
    snake.direction = record.init_direction;
    Point tmp = tail;
    for (int i = 1; i <= initlen; i++, tmp += snake.direction) {
        snake.points.push_front(tmp);
        map[tmp.x][tmp.y] = true;
    }
    
    food = record.food[record.food_i++];
}

void Game::newFood() {
    int food_x, food_y;
    do {
        food_x = random(1, height);
        food_y = random(1, width);
    } while (map[food_x][food_y]);
    food = Point(food_x, food_y);
    
	record.food.push_back(food);
}

int Game::next() {
    if (record.operation_i == (int)record.operation.size()) {
        status = -2;
        return -2;
    }
    time++;
    Point new_direction = numToDirection(record.operation[record.operation_i++]);
    if (new_direction == -snake.direction) {
        status = 1;
        return 1;
    }

    snake.direction = new_direction;
    Point head = snake.points.front();
    Point tail = snake.points.back();
    Point new_head = head + snake.direction;
    if (checkOver(new_head)) {
        status = -1;
        return -1;
    }
    // 先把尾巴删掉，防止将环形蛇误判为死亡 
    snake.points.pop_back();
    map[tail.x][tail.y] = false;
    snake.points.push_front(new_head);
    map[new_head.x][new_head.y] = true;

    if (new_head == food) {
        score++;
        // 吃食物后蛇的长度要增加，所以把刚才删掉的尾巴再加回来 
        snake.points.push_back(tail);
    	map[tail.x][tail.y] = true;
        food = record.food[record.food_i++];
    }
    status = 0;
    return 0;
}

Game::Point Game::numToDirection(int num) {
	Point direction;
	switch(num) {
        case 1:
            direction = Point(-1, 0);
            break;
        case 2:
            direction = Point(1, 0);
            break;
        case 3:
            direction = Point(0, -1);
            break;
        case 4:
            direction = Point(0, 1);
            break;
    }
    return direction;
}

void Game::outputData(bool arr[]) {
    int p = 0;
    for (int i = 1; i <= height; i++)
        for (int j = 1; j <= width; j++) {
            arr[p] = map[i][j];
            if (i == food.x && j == food.y)
                arr[p] = true;
            p++; 
        }    
}

void Game::saveRecord(const char *filename) {
    FILE *fp = fopen(filename, "w");
    fprintf(fp, "%d %d %d\n\n", height, width, initlen);
    fprintf(fp, "%d %d\n%d %d\n\n", record.init_tail.x, record.init_tail.y, record.init_direction.x, record.init_direction.y);
    fprintf(fp, "%d\n", (int)record.operation.size());
    for (int i = 0; i < (int)record.operation.size(); i++)
        fprintf(fp, "%d\n", record.operation[i]);
    fprintf(fp, "\n");
    fprintf(fp, "%d\n", (int)record.food.size());
    for (int i = 0; i < (int)record.food.size(); i++)
        fprintf(fp, "%d %d\n", record.food[i].x, record.food[i].y);
    fclose(fp);
}

#endif